package com.plateno.service;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.springframework.stereotype.Service;
import org.wltea.analyzer.lucene.IKAnalyzer;

@Service
public class IKAnalzyerService {

	@SuppressWarnings({ "resource", "unused" })
	public Set<String> analzyer(String text) {
		Set<String> result = new HashSet<String>();
		long st = System.currentTimeMillis();
		if (StringUtils.isEmpty(text)) {
			return result;
		}
		// 构建IK分词器，使用smart分词模式
		Analyzer analyzer = new IKAnalyzer(true);
		// String
		// text="琶洲等于敏感词库，HashMap对象在内存中占用的是同一个地址，所以此nowMap对象的变化，sensitiveWordMap对象也会跟着改变为了保证在业务高峰期，线上系统也能保证一定的弹性和稳定性，最有效的方案就是进行服务降级了，而限流就是降级系统最常采用的方案之一。限流即流量限制，或者高大上一点，叫做流量整形，限流的目的是在遇到流量高峰期或者流量突增（流量尖刺）时，把流量速率限制在系统所能接受的合理范围之内，不至于让系统被高流量击垮。其实，服务降级系统中的限流并没有我们想象的那么简单，第一，限流方案必须是可选择的，没有任何方案可以适用所有场景，每种限流方案都有自己适合的场景，我们得根据业务和资源的特性和要求来选择限流方案；第二，限流策略必须是可配的，对策略调优一定是个长期的过程，这里说的策略，可以理解成建立在某个限流方案上的一套相关参数。目前有几种常见的限流方式：通过限制单位时间段内调用量来限流通过限制系统的并发调用程度来限流使用漏桶（Leaky
		// Bucket）算法来进行限流使用令牌桶（Token
		// Bucket）算法来进行限流下面我们来一起讨论各种限流方式的设计和具体使用场景。对于第1种，通过限制某个服务的单位时间内的调用量来进行限流。从字面上，确实很容易理解，我们需要做的就是通过一个计数器统计单位时间段某个服务的访问量，如果超过了我们设定的阈值，则该单位时间段内则不允许继续访问、或者把接下来的请求放入队列中等待到下一个单位时间段继续访问。这里，计数器在需要在进入下一个单位时间段时先清零。我们来看看在Java语言中，这种方式具体应该如何做，第一步我们需要做的就是确定这个单位时间段有多长，肯定不能太长，太长将会导致限流的效果变得不够“敏感”，因为我们知道，进入限流阶段后，如果采用的手段是不允许继续访问，那么在该单位时间段内，该服务是不可用的，比如我们把单位时间设置成1小时，如果在第29分钟，该服务的访问量就达到了我们设定的阈值，那么在接下来的31分钟，该服务都将变得不可用，这无形SO
		// BAD!!如果单位时间段设置得太短，越短的单位时间段将导致我们的阈值越难设置，比如1秒钟，因为高峰期的1秒钟和低峰期的1秒钟的流量有可能相差百倍甚至千倍，同时过短的单位时间段也对限流代码片段提出了更高要求，限流部分的代码必须相当稳定并且高效！最优的单位时间片段应该以阈值设置的难易程度为标准，比如我们的监控系统统计的是服务每分钟的调用量，所以很自然我们可以选择1分钟作为时间片段，因为我们很容易评估每个服务在高峰期和低峰期的分钟调用量，并可以通过服务在每分钟的平均耗时和异常量来评估服务在不同单位时间段的服务质量，这给阈值的设置提供了很好的参考依据。当单位时间段和阈值已经确定，接下来就该考虑计数器的实现了，最快能想到的就是AtomicLong了，对于每次服务调用，我们可以通过AtomicLong#incrementAndGet()方法来给计数器加1并返回最新值，我们可以通过这个最新值和阈值来进行比较来看该服务单位时间段内是否超过了阈值。这里，如何设计计数器是个关键，假设单位时间段为1分钟，我们可以做一个环状结构的计数器，如下：";
		// 获取Lucene的TokenStream对象
		TokenStream ts = null;
		try {
			ts = analyzer.tokenStream("myfield", new StringReader(text));
			// 获取词元位置属性
			OffsetAttribute offset = ts.addAttribute(OffsetAttribute.class);
			// 获取词元文本属性
			CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
			// 获取词元文本属性
			TypeAttribute type = ts.addAttribute(TypeAttribute.class);

			// 重置TokenStream（重置StringReader）
			ts.reset();
			// 迭代获取分词结果
			while (ts.incrementToken()) {
				// System.out.println(offset.startOffset() + " - " +
				// offset.endOffset() + " : " + term.toString() + " | " +
				// type.type());
				result.add(term.toString());
			}

			// 关闭TokenStream（关闭StringReader）
			ts.end(); // Perform end-of-stream operations, e.g. set the final
						// offset.
			System.out.println("IKanalzyer耗时：" + (System.currentTimeMillis() - st));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 释放TokenStream的所有资源
			if (ts != null) {
				try {
					ts.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;

	}

}
